function [Alpha, RAReturn] = portalpha(Asset, Benchmark, Cash, Choice)
%PORTALPHA Compute risk-adjusted alphas and returns for one or more assets.
%	Given NUMSERIES assets with NUMSAMPLES returns in a NUMSAMPLES x NUMSERIES
%	matrix Asset, a NUMSAMPLES vector of Benchmark returns, and either a scalar
%	Cash return or a NUMSAMPLES vector of Cash returns, compute risk-adjusted
%	alphas and returns for one or more methods specified by Choice.
%
%	portalpha(Asset, Benchmark);
%	portalpha(Asset, Benchmark, Cash);
%	portalpha(Asset, Benchmark, Cash, Choice);
%	Alpha = portalpha(Asset, Benchmark, Cash, Choice);
%	[Alpha, RAReturn] = portalpha(Asset, Benchmark, Cash, Choice);
%
% Inputs:
%	Asset - NUMSAMPLES x NUMSERIES matrix with NUMSAMPLES observations of
%		asset returns for NUMSERIES asset return series.
%	Benchmark - A NUMSAMPLES vector of returns for a benchmark asset. The
%		periodicity must be the same as the periodicity of Asset, e.g., if Asset
%		is monthly data, then Benchmark should be monthly returns.
%
% Optional Inputs:
%	Cash - Either a scalar return for a riskless asset or a vector of asset
%		returns to be a proxy for a "riskless" asset. In either case, the
%		periodicity must be the same as the periodicity of Asset, e.g., if
%		Asset is monthly data, then Cash must be monthly returns. If no value is
%		supplied, the default value for Cash returns is 0. 
%	Choice - A number, string, or cell array of numbers or strings to indicate
%		one or more measures to be computed from among a number of risk-adjusted
%		alphas and return measures. The number of choices selected in Choice is
%		NUMCHOICES. The current list of choices is given in the following table:
%
%		Code		Description
%		------		----------------------------------
%		'xs'		Excess Return (no risk adjustment)
%		'sml'		Security Market Line
%		'capm'		Jensen's Alpha
%		'mm'		Modigliani & Modigliani
%		'gh1'		Graham-Harvey 1
%		'gh2'		Graham-Harvey 2
%		------		----------------------------------
%		'all'		Compute all measures
%
%		Choices are specified by the Code from the table (e.g., to select the
%		Modigliani & Modigliani measure, Choice = 'mm'). A single choice is
%		either a string or a scalar cell array with a single Code from the
%		table. Multiple choices can be selected with a cell array of choice
%		Codes (e.g., to select both Graham-Harvey measures, Choice = { 'gh1',
%		'gh2' }). To select all choices, specify Choice = 'all'. If no value is
%		supplied, the default choice is to compute the excess return with Choice
%		= 'xs'. Choices are not case-sensitive.
%
% Ouptuts:
%	Alpha - A NUMCHOICES x NUMSERIES matrix of risk-adjusted alphas for each
%		series in Asset with each row corresponding to a specified measure in
%		Choice.
%	RAReturn - A NUMCHOICES x NUMSERIES matrix of risk-adjusted returns for each
%		series in Asset with each row corresponding to a specified measure in
%		Choice.
%
% Notes:
%	NaN values in the data are ignored and, if NaNs are present, some results
%	could be unpredictable.
%
%	Strict intrepretation of some of the measures imposes conditions on the
%	types of Benchmark or Cash inputs. In addition, although the alphas are
%	comparable across measures, risk-adjusted returns depend upon whether the
%	Asset or Benchmark is levered or unlevered to match its risk with the
%	alternative. Consult the tutorial for details.
%
%	If Choice = 'all', the order of rows in Alpha and RAReturn follows the order
%	in the table. In addition, Choice = 'all' overrides all other Choices.
%
% References:
% [1] John Lintner, "The Valuation of Risk Assets and the Selection of Risky
% Investments in Stocks Portfolios and Capital Budgets," Review of Economics and
% Statistics, Vol. 47, No. 1, February 1965, pp. 13-37.
%
% [2] John R. Graham and Campbell R. Harvey, "Market Timing Ability and
% Volatility Implied in Investment Newsletters' Asset Allocation
% Recommendations," Journal of Financial Economics, Vol. 42, 1996, pp. 397-421.
%
% [3] Franco Modigliani and Leah Modigliani, "Risk-Adjusted Performance: How to
% Measure It and Why," Journal of Portfolio Management, Vol. 23, No. 2, Winter
% 1997, pp. 45-54.
%
% [4] Jan Mossin, "Equilibrium in a Capital Asset Market," Econometrica, Vol. 34,
% No. 4, October 1966, pp. 768-783.
%
% [5] William F. Sharpe, "Capital Asset Prices: A Theory of Market Equilibrium
% under Conditions of Risk," Journal of Finance, Vol. 19, No. 3, September 1964,
% pp. 425-442.
%
%	See also: inforatio, sharpe

%	Copyright 1995-2009 The MathWorks, Inc.
%	$Revision: 1.1.6.5 $   $Date: 2009/03/09 19:12:29 $

% Step 1 - check arguments

if nargin < 2 || isempty(Asset) || isempty(Benchmark)
	error('Finance:portalpha:MissingInputArg', ...
		'Missing required input arguments Asset or Benchmark.');
end

if ~isscalar(Asset) && isvector(Asset) && isa(Asset,'double')
	Asset = Asset(:);
	[nF, n] = size(Asset);
elseif ndims(Asset) == 2 && min(size(Asset)) > 1 && isa(Asset,'double')
	[nF, n] = size(Asset);
else
	error('Finance:portalpha:InvalidInputArg', ...
 		'Invalid format for Asset returns. Must be a vector or matrix.');
end

if ~isscalar(Benchmark) && isvector(Benchmark) && isa(Benchmark,'double')
	Benchmark = Benchmark(:);
	nM = size(Benchmark, 1);
	if nM ~= nF
		error('Finance:portalpha:InconsistentDims', ...
			'Number of samples for Asset (%d) and Benchmark (%d) differ.',nF,nM);
	end
else
	error('Finance:portalpha:InvalidInputArg', ...
		'Invalid format for Benchmark returns. Must be a scalar or vector.');
end

if nargin < 3 || isempty(Cash)
	warning('Finance:portalpha:DefaultInputArg', ...
		'No Cash return specified. Will assume return is 0.');
	Cash = zeros(nF,1);
else
	if isscalar(Cash) && isa(Cash,'double')
		Cash = Cash .* ones(nF, 1);
	elseif isvector(Cash) && isa(Cash,'double')
		Cash = Cash(:);
		nC = size(Cash, 1);
		if nC ~= nF
			error('Finance:portalpha:InconsistentDims', ...
				'Number of samples for Asset (%d) and Cash (%d) differ.',nF,nC);
		end
	else
		error('Finance:portalpha:InvalidInputArg', ...
			'Invalid format for Cash returns. Must be a scalar or vector.');
	end
end

if nargin < 4 || isempty(Choice)		% default Choice is excess return
	Choice = {'xs'};
end

if ischar(Choice)
	if strcmpi(Choice,'all')
		Choice = {'xs', 'sml', 'capm', 'mm', 'gh1', 'gh2'};
	else
		Choice = {Choice};
	end
elseif isvector(Choice) && iscellstr(Choice)
	if any(strcmpi(Choice,'all'))
		Choice = {'xs', 'sml', 'capm', 'mm', 'gh1', 'gh2'};
	end
else
	error('Finance:portalpha:InvalidInputArg', ...
		'Invalid format for Choice. Must be string or a vector cell array of strings.');
end

m = numel(Choice);

% Step 2 - initialization

Alpha = zeros(m,n);
RAReturn = zeros(m,n);

mF = zeros(1,n);
sF = zeros(1,n);
rFC = zeros(1,n);
rFM = zeros(1,n);

% Step 3 - compute basic statistics

mM = nanmean(Benchmark);
sM = nanstd(Benchmark, 1);

mC = nanmean(Cash);
sC = nanstd(Cash, 1);

if sM == 0 || sC == 0
	rMC = 0;
else
	Covar = nancov(Benchmark, Cash, 1);
	rMC = Covar(1,2)/sqrt(Covar(1,1)*Covar(2,2));
end

for k = 1:n
	mF(k) = nanmean(Asset(:,k));
	sF(k) = nanstd(Asset(:,k), 1);

	if sF(k) == 0 || sC == 0
		rFC(k) = 0;
	else
		Covar = nancov(Asset(:,k), Cash, 1);
		rFC(k) = Covar(1,2)/sqrt(Covar(1,1)*Covar(2,2));
	end
	
	if sF(k) == 0 || sM == 0
		rFM(k) = 0;
	else
		Covar = nancov(Asset(:,k), Benchmark, 1);
		rFM(k) = Covar(1,2)/sqrt(Covar(1,1)*Covar(2,2));
	end
end

% Step 3 - compute measures

ii = 0;
while ii < m
	ii = ii + 1;
	switch lower(Choice{ii})
		case {'xs', 'excess'}
			Alpha(ii,:) = mF - mM;
			RAReturn(ii,:) = mF;
		case {'sml', 'ml'}
			if sM == sC
				warning('Finance:portalpha:SMLNonExistence', ...
					'SML measure does not exist for all assets since Benchmark and Cash have identical risk.');
				Alpha(ii,:) = NaN;
			else
				for k = 1:n
					Alpha(ii,k) = (mF(k) - mC) - (sF(k) - sC)*(mM - mC)/(sM - sC);
				end
			end
			RAReturn(ii,:) = mF - Alpha(ii,:);
		case {'capm','jensen'}
			if nanmax(Benchmark) == nanmin(Benchmark)
				warning('Finance:portalpha:CAPMNonExistence', ...
					'CAPM measure does not exist for all assets since Benchmark risk is 0.');
				Alpha(ii,:) = NaN;
			else
				for k = 1:n
					Alpha(ii,k) = (mF(k) - mC) - (rFM(k)*sF(k)/sM)*(mM - mC);
				end
			end
			RAReturn(ii,:) = mF - Alpha(ii,:);
		case {'mm', 'm2', 'modigliani'}
			for k = 1:n
				if nanmax(Asset(:,k)) == nanmin(Asset(:,k))
					warning('Finance:portalpha:MMNonExistence', ...
						'MM measure does not exist for asset %d. Fund risk too low.',k);
					Alpha(ii,k) = NaN;
				else
					Alpha(ii,k) = (sM/sF(k))*(mF(k) - mC) - (mM - mC);
				end
			end
			RAReturn(ii,:) = Alpha(ii,:) + mM;
		case 'gh1'
			L1 = sC*sC - rMC*sM*sC;
			L2 = sM*sM - rMC*sM*sC;
			if abs(L1 + L2) < eps
				% Benchmark and Cash have equal risks with either +1 correlation
				% or zero risk.
				warning('Finance:portalpha:GH1NonExistence', ...
					'GH1 measure does not exist for all assets.');
				Alpha(ii,:) = NaN;
			else
				for k = 1:n
					Disc = L1*L1 + (L1 + L2)*(sF(k)*sF(k) - sC*sC);
					if Disc < 0
						warning('Finance:portalpha:GH1NonExistence', ...
							'GH1 measure does not exist for asset %d. Asset risk too low.',k);
						Alpha(ii,k) = NaN;
					else
						Factor = (L1 + sqrt(abs(Disc)))/(L1 + L2);
						Alpha(ii,k) = (mF(k) - mC) - Factor*(mM - mC);
					end
				end
			end
			RAReturn(ii,:) = mF - Alpha(ii,:);
		case 'gh2'
			for k = 1:n
				L1 = sC*sC - rFC(k)*sF(k)*sC;
				L2 = sF(k)*sF(k) - rFC(k)*sF(k)*sC;
				Disc = L1*L1 + (L1 + L2)*(sM*sM - sC*sC);
				if abs(L1 + L2) < eps
					% Asset and Cash have equal risks with either +1 correlation
					% or zero risk.
					warning('Finance:portalpha:GH2NonExistence', ...
						'GH2 measure does not exist for asset %d.',k);
					Alpha(ii,k) = NaN;
				elseif Disc < 0
					warning('Finance:portalpha:GH2NonExistence', ...
						'GH2 measure does not exist for asset %d. Benchmark risk too low.',k);
					Alpha(ii,k) = NaN;
				else
					Factor = (L1 + sqrt(abs(Disc)))/(L1 + L2);
					Alpha(ii,k) = Factor*(mF(k) - mC) - (mM - mC);
				end
			end
			RAReturn(ii,:) = Alpha(ii,:) + mM;
		otherwise
			error('Finance:portalpha:InvalidChoice', ...
				'Invalid choice selected. See portalpha help for a list of choices.');
	end
end
